package cn.tangsihai.user.service.impl;

import cn.tangsihai.basic.exception.MyException;
import cn.tangsihai.basic.util.AjaxResult;
import cn.tangsihai.basic.util.HttpClientUtils;
import cn.tangsihai.basic.util.MD5Utils;
import cn.tangsihai.basic.util.StrUtils;
import cn.tangsihai.user.constant.UserConstant;
import cn.tangsihai.user.domain.Logininfo;
import cn.tangsihai.user.domain.User;
import cn.tangsihai.user.domain.WxUser;
import cn.tangsihai.user.dto.LoginDto;
import cn.tangsihai.user.mapper.LogininfoMapper;
import cn.tangsihai.user.mapper.UserMapper;
import cn.tangsihai.user.mapper.WxUserMapper;
import cn.tangsihai.user.service.ILoginService;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.annotation.JsonAppend;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 登录业务类
 */
@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxUserMapper wxUserMapper;
    @Autowired
    private UserMapper userMapper;

    /**
     * 账号登录
     * @param loginDto
     * @return
     */
    @Override
    public Map<String, Object> account(LoginDto loginDto) {
        // 校验
        // 1.非空
        if (StringUtils.isEmpty(loginDto.getPassword())
                || StringUtils.isEmpty(loginDto.getType())
                || StringUtils.isEmpty(loginDto.getUsername())){
            throw new MyException("参数不能为空");
        }
        // 判断用户是否存在，根据用户名和type在logininfo表中查询用户登录信息
        Logininfo logininfo = logininfoMapper.findByUsernameAndType(loginDto);
        if (logininfo == null){ // 用户不存在
            throw new MyException("该用户不存在");
        }
        // 用户存在，比较密码是否相同，注：添加用户的时候密码是加密加盐，比较的时候也应该加密加盐比较
        String dbSalt = logininfo.getSalt(); // 获取数据库中密码的盐值
        String dbPassword = logininfo.getPassword(); // 获取数据库中加密后的密码，为了比较
        // 将登录时的密码加密加盐
        String nowPassword = MD5Utils.encrypByMd5(loginDto.getPassword() + dbSalt);
        // 比较密码是否一致
        if (!nowPassword.equals(dbPassword)){ // 密码不一致
            throw new MyException("密码错误");
        }
        // 密码一致，将登录信息存入redis中，方便拦截使用
        String token = UUID.randomUUID().toString(); // 生成永不重复的随机字符串，将其作为存入redis的用户信息的key--token
        // 防止密码外泄
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        // 将登录信息存入redis中，30分钟无操作后过期,
        // 注：logininfo是一个对象，想要存入redis中，对象的实体类必须要序列化，即实现Serializable接口
        redisTemplate.opsForValue().set(token, logininfo, 30 , TimeUnit.MINUTES);
        // 准备map
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("logininfo", logininfo);
        // 将map响应到controller层
        return map;
    }

    /**
     * 微信登录
     * @param params
     * @return
     */
    @Override
    @Transactional
    public AjaxResult wechat(Map<String, String> params) {
        // 1.检验授权码code非空
        String code = params.get("code");
        if (StringUtils.isEmpty(code)){
            throw new MyException("授权码不存在，紧急处理中");
        }
        // 2.用工具类httpClient向微信端发起请求，通过授权码code获取含有令牌token的数据字符串
        String wxToken = HttpClientUtils.httpGet(UserConstant.TOKEN_BY_CODE
                .replace("APPID", UserConstant.APPID)
                .replace("SECRET", UserConstant.SECRET)
                .replace("CODE", code));

        // 2.1.将json格式字符串通过fastJSON工具转换成json格式对象
        JSONObject jsonObject = JSONObject.parseObject(wxToken);

        // 2.2.从json格式对象中获取令牌access_token, open_id，注：转换成字符串
        String accessToken = jsonObject.getString("access_token");
        String openId = jsonObject.getString("openid");

        // 3.通过opendId查询wxUser表中数据判断，若有数据，且user_id字段不为空，免密登录
        WxUser wxUser = wxUserMapper.findByOpenId(openId);
        if (wxUser != null && wxUser.getUser_id() != null){
            Logininfo logininfo = logininfoMapper.findByUserId(wxUser.getUser_id());

            String token = UUID.randomUUID().toString(); // 生成永不重复的随机字符串，将其作为存入redis的用户信息的key--token
            // 防止密码外泄
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            // 将登录信息存入redis中，30分钟无操作后过期,
            // 注：logininfo是一个对象，想要存入redis中，对象的实体类必须要序列化，即实现Serializable接口
            redisTemplate.opsForValue().set(token, logininfo, 30 , TimeUnit.MINUTES);
            // 准备map
            Map<String, Object> map = new HashMap<>();
            map.put("token", token);
            map.put("logininfo", logininfo);
            // 将map响应到controller层
            return AjaxResult.me().setResultObj(map);
        } else { // 4.否则，跳转到用户绑定页面登记信息。
            String param = "?accessToken=" + accessToken + "&openId=" + openId;
            return AjaxResult.me().setMsg("第一次登录，请绑定手机号").setResultObj(param);
        }
    }

    /**
     * 第一次微信登录绑定业务
     * @param map
     * @return
     */
    @Override
    @Transactional
    public AjaxResult bander(Map<String, String> map) {
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String accessToken = map.get("accessToken");
        String openId = map.get("openId");

        // 校验非空
        if (StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(verifyCode)
                || StringUtils.isEmpty(accessToken)
                || StringUtils.isEmpty(openId)){
            throw new MyException("手机号或验证码不能为空");
        }

        // 发送验证码，注：已经在VerifycodeController.java中实现
        // 校验验证码是否过期
        Object temCode = redisTemplate.opsForValue().get(UserConstant.BANDER_PREFIX + phone);
        if (temCode == null ){
            throw new MyException("验证码已经过期，请重新获取");
        } else {
            // 获取redis中的验证码
            String tcode = temCode.toString().split(":")[1];
            if (!tcode.equalsIgnoreCase(verifyCode)){
                throw new MyException("验证码错误有，请输入正确验证码");
            }
        }
        // 手机号是否注册过
        User temUser = userMapper.findByPhone(phone);
        User user = null;
        Logininfo logininfo = null;
        if (temUser != null){ // 注册过，直接绑定
            logininfo = logininfoMapper.findByIdInUser(temUser.getLogininfo_id());
        } else { // 没注册，手动绑定
            // 生成user，logininfo，wxuser
            user = initUser(phone);
            logininfo = initLogininfo(user);
            // 将数据保存在数据库中，
            // 先保存logininfo，获取自增长id
            logininfoMapper.add(logininfo);
            // 再保存user，获取自增长id
            user.setLogininfo_id(logininfo.getId());
            userMapper.add(user);
        }
        // 发送请求到微信端获取用户信息
        String wxStr = HttpClientUtils.httpGet(UserConstant.USER_BY_TOKEN
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId));
        // 生成wxUser数据
        WxUser wxUser = initWxUser(wxStr, user);
        // 最后保存wxUser
        wxUserMapper.add(wxUser);
        // 将登录信息存入redis中,并将信息返回给前端
        // 密码一致，将登录信息存入redis中，方便拦截使用
        String token = UUID.randomUUID().toString(); // 生成永不重复的随机字符串，将其作为存入redis的用户信息的key--token
        // 防止密码外泄
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        // 将登录信息存入redis中，30分钟无操作后过期,
        // 注：logininfo是一个对象，想要存入redis中，对象的实体类必须要序列化，即实现Serializable接口
        redisTemplate.opsForValue().set(token, logininfo, 30 , TimeUnit.MINUTES);
        // 准备map
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("token", token);
        resultMap.put("logininfo", logininfo);
        // 将map响应到controller层
        return AjaxResult.me().setResultObj(resultMap);
    }
// ===========================准备user，logininfo，wxuser=====================================

    /**
     * 生成wxUser
     * @param wxStr
     * @param user
     * @return
     */
    private WxUser initWxUser(String wxStr, User user) {
        // 将json格式字符串通过fastJSON工具转换成json格式对象
        JSONObject jsonObject = JSONObject.parseObject(wxStr);
        WxUser wxUser = new WxUser();
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setAddress(jsonObject.getString("city"));
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        wxUser.setUser_id(user.getId());
        return wxUser;
    }

    /**
     * 生成loginingo
     * @param user
     * @return
     */
    private Logininfo initLogininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        logininfo.setDisable(1);
        return logininfo;
    }

    /**
     * 生成user
     * @param phone
     * @return
     */
    private User initUser(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setUsername(phone);
        String salt = StrUtils.getComplexRandomString(32);
        user.setSalt(salt);
        user.setPassword(MD5Utils.encrypByMd5(1 + salt));
        user.setState(1);
        return user;
    }
}
